<!DOCTYPE html>
            
<HTML>
<HEAD>
<meta name="booktitle" content="Developing Applications With Objective Caml" >
 <meta charset="ISO-8859-1"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<META name="GENERATOR" content="hevea 1.05-7 of 2000-02-24">
<META NAME="Author" CONTENT="Christian.Queinnec@lip6.fr">
<LINK rel=stylesheet type="text/css" href="videoc-ocda.css">
<script language="JavaScript" src="videoc.js"><!--
//--></script>
<TITLE>
 Synchronization of Processes
</TITLE>
</HEAD>
<BODY class="regularBody">
<A HREF="book-ora175.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora177.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
<HR>

<H2> Synchronization of Processes</H2>
<A NAME="@concepts351"></A>
In the setting of processes sharing a common zone of memory,
the word ``concurrency'' carries its full meaning: the various processes
involved are compete for access to the unique resource
of the memory<A NAME="text46" HREF="book-ora182.html#note46"><SUP><FONT SIZE=2>2</FONT></SUP></A>.
To the problem of division of resources, is added
that of the lack of control of the alternation and of the execution
times of the concurrent processes.<BR>
<BR>
The system which manages the collection of processes can at any moment
interrupt a calculation in progress. Thus when two processes
cooperate, they must be able to guarantee the integrity of the
manipulations of certain shared data. For this, a process should be
able to remain owner of these data as long as it has not completed a
calculation or any other operation (for example, an acquisition of
data from a peripheral). To guarantee the exclusivity of access to
the data to a single process, we set up a mechanism called
mutual exclusion.<BR>
<BR>
<A NAME="toc259"></A>
<H3> Critical Section and Mutual Exclusion</H3>
<A NAME="@concepts352"></A>
<A NAME="@concepts353"></A>The mechanisms of mutual exclusion are implemented with the help of
particular data structures called mutexes. The operations on
mutexes are limited to their creation, their setting, and their
disposal. A mutex is the smallest item of data shared by a collection
of concurrent processes. Its manipulation is always exclusive. To
the notion of exclusivity of manipulation of a mutex is added that of
exclusivity of possession: only the process which has taken a
mutex can free it; if other processes wish to use
the mutex, then they must wait for it to be released by the
process that is holding it.<BR>
<BR>
<A NAME="toc260"></A>
<H3> <TT>Mutex</TT> Module</H3>
<A NAME="@fonctions447"></A>
Module <TT>Mutex</TT> is used to create mutexes between processes
related by mutual exclusion on an area of memory. We will
illustrate their use with two small classic examples of concurrency.<BR>
<BR>
<A NAME="@fonctions448"></A>
<A NAME="@fonctions449"></A>
<A NAME="@fonctions450"></A>
The functions of creation, locking, and unlocking of mutexes are:


<PRE><BR># Mutex.create<CODE> </CODE>;;<BR><CODE>- : unit -&gt; Mutex.t = &lt;fun&gt;</CODE><BR># Mutex.lock<CODE> </CODE>;;<BR><CODE>- : Mutex.t -&gt; unit = &lt;fun&gt;</CODE><BR># Mutex.unlock<CODE> </CODE>;;<BR><CODE>- : Mutex.t -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
<A NAME="@fonctions451"></A>
There exists a variant of mutex locking that is non-blocking:


<PRE><BR># Mutex.try_lock;;<BR><CODE>- : Mutex.t -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>

If the mutex is already locked, the function returns <B>false</B>.
Otherwise, the function locks the mutex and returns
<B>true</B>.<BR>
<BR>

<H4> The Dining Philosophers</H4>
This little story, due to Dijkstra, illustrates a pure problem of
resource allocation. It goes as follows: <BR>
<BR>
``Five oriental philosophers divide their time between study and
coming to the refectory to eat a bowl of rice. The room devoted to
feeding the philosophers contains nothing but a single round
table on which there is a large dish of rice (always full), five
bowls, and five chopsticks.''<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora079.gif">
<BR>
<BR>
<DIV ALIGN=center>Figure 19.1: The Table of the Dining Philosophers</DIV><BR>

<A NAME="fig-philosophes"></A>
</DIV>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>
As we can see in the figure <A HREF="book-ora176.html#fig-philosophes">19.1</A>, a philosopher who
takes his two chopsticks beside his bowl stops his neighbours from
doing the same. When he puts down one of his chopsticks, his
neighbour, famished, can grab it. If needs be, this latter should wait
until the other chopstick is available. Here the chopsticks are the
resources to be allocated.<BR>
<BR>
To simplify things, we suppose that each philosopher habitually comes
to the same place at the table. We model the five chopsticks as five mutexes stored
in a vector <TT>b</TT>.


<PRE><BR># <B>let</B><CODE> </CODE>b<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>b0<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Array.create<CODE> </CODE><CODE>5</CODE><CODE> </CODE><TT>(</TT>Mutex.create()<TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>for</B><CODE> </CODE>i<CODE>=</CODE><CODE>1</CODE><CODE> </CODE><B>to</B><CODE> </CODE><CODE>4</CODE><CODE> </CODE><B>do</B><CODE> </CODE>b0<CODE>.</CODE><TT>(</TT>i<TT>)</TT><CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>Mutex.create()<CODE> </CODE><B>done</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>b0<CODE> </CODE>;;<BR><CODE>val b : Mutex.t array = [|&lt;abstr&gt;; &lt;abstr&gt;; &lt;abstr&gt;; &lt;abstr&gt;; &lt;abstr&gt;|]</CODE><BR>

</PRE>
<BR>
<BR>
Eating and meditation are simulated by a suspension of processes.


<PRE><BR># <B>let</B><CODE> </CODE>meditation<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Thread.delay<CODE> </CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>eating<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Thread.delay<CODE> </CODE>;;<BR><CODE>val meditation : float -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val eating : float -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We model a philosopher by a function which executes an infinite
sequence of actions from Dijsktra's story. Taking a chopstick
is simulated by the acquisition of a mutex, thus a single philosopher
can hold a given chopstick at a time. We introduce a little time of
reflection between taking and dropping of each of the two
chopsticks while a number of output commands track the activity of the
philosopher.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>philosopher<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ii<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>i<CODE>+</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE><B>mod</B><CODE> </CODE><CODE>5</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>meditation<CODE> </CODE><CODE>3</CODE><CODE>.</CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>b<CODE>.</CODE><TT>(</TT>i<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Philosopher (%d) takes his left-hand chopstick"</CODE><CODE> </CODE>i<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>" and meditates a little while more\n"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>meditation<CODE> </CODE><CODE>0</CODE><CODE>.</CODE><CODE>2</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>b<CODE>.</CODE><TT>(</TT>ii<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Philosopher (%d) takes his right-hand chopstick\n"</CODE><CODE> </CODE>i;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>eating<CODE> </CODE><CODE>0</CODE><CODE>.</CODE><CODE>5</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>b<CODE>.</CODE><TT>(</TT>i<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Philosopher (%d) puts down his left-hand chopstick"</CODE><CODE> </CODE>i;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>" and goes back to meditating\n"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>meditation<CODE> </CODE><CODE>0</CODE><CODE>.</CODE><CODE>1</CODE><CODE>5</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>b<CODE>.</CODE><TT>(</TT>ii<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Philosopher (%d) puts down his right-hand chopstick\n"</CODE><CODE> </CODE>i<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val philosopher : int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We can test this little program by executing:


<PRE><BR><B>for</B><CODE> </CODE>i<CODE>=</CODE><CODE>0</CODE><CODE> </CODE><B>to</B><CODE> </CODE><CODE>4</CODE><CODE> </CODE><B>do</B><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>Thread.create<CODE> </CODE>philosopher<CODE> </CODE>i<TT>)</TT><CODE> </CODE><B>done</B><CODE> </CODE>;<BR><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE>Thread.delay<CODE> </CODE><CODE>5</CODE><CODE>.</CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
We suspend, in the infinite loop <B>while</B>, the main process
in order to increase the chances of the philosopher processes to run.
We use randomly chosen delays in the activity loop with the aim of
creating some disparity in the parallel execution of the processes.<BR>
<BR>

<H5> Problems of the na�ve solution.</H5>
<A NAME="@concepts354"></A>
A terrible thing can happen to our philosophers: they all arrive at
the same time and seize the chopstick on their left. In this case we
are in a situation of dead-lock. None of the philosophers
can eat! We are in a situation of starvation.<BR>
<BR>
To avoid this, the philosophers can put down a chopstick if they do not
manage to take the second one. This is highly courteous, but still
allows two philosophers to gang up against a third to stop him from
eating, by not letting go of their chopsticks, except the ones that
their other neighbour has given them. There exist numerous solutions
to this problem. One of them is the object of the exercise on page
<A HREF="book-ora179.html#exo-philo">??</A>.<BR>
<BR>

<H4> Producers and Consumers <FONT COLOR=navy>I</FONT></H4>
<A NAME="@concepts355"></A>
The pair of producers-consumers is a classic example of
concurrent programming. A group of processes, designated the
producers, are in charge of storing data in a queue: a second group,
the consumers, is in charge of removing it. Each intervening party
excludes the others.<BR>
<BR>
We implement this scheme using a queue shared between the producers
and the consumers. To guarantee the proper operation of the system,
the queue is manipulated in mutual exclusion in order to guarantee the
integrity of the operations of addition and removal.<BR>
<BR>
<TT>f</TT> is the shared queue, and <TT>m</TT>&nbsp; is the mutex. 


<PRE><BR># <B>let</B><CODE> </CODE>f<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Queue.create<CODE> </CODE>()<CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE><CODE> </CODE>m<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Mutex.create<CODE> </CODE>()<CODE> </CODE>;;<BR><CODE>val f : '_a Queue.t = &lt;abstr&gt;</CODE><BR><CODE>val m : Mutex.t = &lt;abstr&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We divide the activity of a producer into two parts: creating a
product (function <TT>produce</TT>) and storing a product (fonction
<TT>store</TT>). Only the operation of storage needs the mutex.


<PRE><BR># <B>let</B><CODE> </CODE>produce<CODE> </CODE>i<CODE> </CODE>p<CODE> </CODE>d<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>incr<CODE> </CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Thread.delay<CODE> </CODE>d<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Producer (%d) has produced %d\n"</CODE><CODE> </CODE>i<CODE> </CODE><CODE>!</CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;;<BR><CODE>val produce : int -&gt; int ref -&gt; float -&gt; unit = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>store<CODE> </CODE>i<CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Queue.add<CODE> </CODE><TT>(</TT>i<CODE>,!</CODE>p<TT>)</TT><CODE> </CODE>f<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Producer (%d) has added its %dth product\n"</CODE><CODE> </CODE>i<CODE> </CODE><CODE>!</CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<BR><CODE>val store : int -&gt; int ref -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The code of the producer is an endless loop of creation and storage.
We introduce a random delay at the end of each iteration in order to desynchronize
the execution.


<PRE><BR># <B>let</B><CODE> </CODE>producer<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>and</B><CODE> </CODE>d<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Random.float<CODE> </CODE><CODE>2</CODE><CODE>.</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>produce<CODE> </CODE>i<CODE> </CODE>p<CODE> </CODE>d<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>store<CODE> </CODE>i<CODE> </CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>2</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val producer : int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The only operation of the consumer is the retrieval of an element of
the queue, taking care that the product is actually there.


<PRE><BR># <B>let</B><CODE> </CODE>consumer<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ip<CODE>,</CODE><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Queue.take<CODE> </CODE>f<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"The consumer(%d) "</CODE><CODE> </CODE>i<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"has taken product (%d,%d)\n"</CODE><CODE> </CODE>ip<CODE> </CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Queue<CODE>.</CODE>Empty<CODE> </CODE>-&gt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"The consumer(%d) "</CODE><CODE> </CODE>i<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE> </CODE><CODE>"has returned empty-handed\n"</CODE><CODE> </CODE><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>2</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val consumer : int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The following test program creates four producers and four consumers.


<PRE><BR><B>for</B><CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>to</B><CODE> </CODE><CODE>3</CODE><CODE> </CODE><B>do</B><BR><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>Thread.create<CODE> </CODE>producer<CODE> </CODE>i<TT>)</TT>;<BR><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>Thread.create<CODE> </CODE>consumer<CODE> </CODE>i<TT>)</TT><BR><B>done</B><CODE> </CODE>;<BR><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE>Thread.delay<CODE> </CODE><CODE>5</CODE><CODE>.</CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
<A NAME="toc261"></A>
<H3> Waiting and Synchronization</H3>
The relation of mutual exclusion is not ``fine'' enough to describe
synchronization between processes. It is not rare that the work of a
process depends on the completion of an action by another process,
thus modifying a certain condition. It is therefore desirable that
the processes should be able to communicate the fact that this
condition might have changed, indicating to the waiting processes to
test it again. The different processes are thus in a relation of
mutual exclusion with communication.<BR>
<BR>
In the preceding example, a consumer, rather than returning
empty-handed, could wait until a producer came to resupply the stock.
This last could signal to the waiting consumer that there is something
to take. The model of waiting on a condition to take a mutex is known
as semaphore.<BR>
<BR>

<H5> Semaphores.</H5>
<A NAME="@concepts356"></A>
A semaphore is an integral variable <I>s</I> which can only take non negative
values. Once <I>s</I> is initialised, the only operations
allowed are: <I>wait</I>(<I>s</I>) and <I>signal</I>(<I>s</I>), written <I>P</I>(<I>s</I>) and <I>V</I>(<I>s</I>),
respectively. They are defined thus, <I>s</I> corresponding to the number
of resources of a given type.
<UL>
<LI>
 <I>wait</I>(<I>s</I>): if <I>s</I> &gt; 0 then <I>s</I> := <I>s</I> -1, otherwise the process, having called <I>wait</I>(<I>s</I>), is suspended.

<LI> <I>signal</I>(<I>s</I>): if a process has been suspended after a prior invocation of <I>wait</I>(<I>s</I>), then wake it up, otherwise <I>s</I> := <I>s</I> + 1.
</UL>A semaphore which only takes the values 0 or 1 is called a
binary semaphore.<BR>
<BR>
<A NAME="toc262"></A>
<H3> <TT>Condition</TT> Module</H3>
<A NAME="@fonctions452"></A>The functions of the module <TT>Condition</TT> implement the
primitives of putting to sleep and waking up processes on a signal. A
signal, in this case, is a variable shared by a collection of
processes. Its type is abstract and the manipulation functions
are:
<A NAME="@fonctions453"></A>
<A NAME="@fonctions454"></A>
<A NAME="@fonctions455"></A>
<A NAME="@fonctions456"></A>
<DL COMPACT=compact>
<DT>
<TT>create</TT><DD>: <I>unit -&gt; Condition.t</I> which creates a new signal.

<DT><TT>signal</TT><DD>: <I>Condition.t -&gt; unit</I> which wakes up one of the processes waiting on a signal.<BR>
<BR>

<DT><TT>broadcast</TT><DD>: <I>Condition.t -&gt; unit</I> which wakes up all of the processes waiting on a signal.<BR>
<BR>

<DT><TT>wait</TT><DD>: <I>Condition.t -&gt; Mutex.t -&gt; unit</I>
which suspends the calling process on the signal passed as the first
argument. The second argument is a mutex used to protect the
manipulation of the signal. It is released, and then reset at each
execution of the function.</DL>
<H4> Producers and Consumers (2)</H4>
<A NAME="pc-II"></A>
We revisit the example of producers and consumeres by using the
mechanism of condition variables to put to sleep a consumer arriving
when the storehouse is empty.<BR>
<BR>
To implement synchronization between waiting consumers and production,
we declare:<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Condition.create<CODE> </CODE>()<CODE> </CODE>;;<BR><CODE>val c : Condition.t = &lt;abstr&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We modify the storage function of the producer by adding to it the
sending of a signal:


<PRE><BR># <B>let</B><CODE> </CODE>store2<CODE> </CODE>i<CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Queue.add<CODE> </CODE><TT>(</TT>i<CODE>,!</CODE>p<TT>)</TT><CODE> </CODE>f<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Producer (%d) has added its %dth product\n"</CODE><CODE> </CODE>i<CODE> </CODE><CODE>!</CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Condition.signal<CODE> </CODE>c<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<CODE> </CODE><BR><CODE>val store2 : int -&gt; int ref -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>producer2<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>d<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Random.float<CODE> </CODE><CODE>2</CODE><CODE>.</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>produce<CODE> </CODE>i<CODE> </CODE>p<CODE> </CODE>d;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>store2<CODE> </CODE>i<CODE> </CODE>p;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>2</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val producer2 : int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The activity of the consumer takes place in two phases: waiting until
a product is available, then taking the product. The mutex is taken
when the wait is finished and it is released when the consumer has
taken its product. The wait takes place on the variable <TT>c</TT>.


<PRE><BR># <B>let</B><CODE> </CODE>wait2<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE>Queue.length<CODE> </CODE>f<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Consumer (%d) is waiting\n"</CODE><CODE> </CODE>i<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Condition.wait<CODE> </CODE>c<CODE> </CODE>m<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val wait2 : int -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>take2<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ip<CODE>,</CODE><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Queue.take<CODE> </CODE>f<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Consumer (%d) "</CODE><CODE> </CODE>i<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"takes product (%d, %d)\n"</CODE><CODE> </CODE>ip<CODE> </CODE>p<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<BR><CODE>val take2 : int -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>consumer2<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>wait2<CODE> </CODE>i;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>take2<CODE> </CODE>i;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>2</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val consumer2 : int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

We note that it is no longer necessary, once a consumer
has begun to wait in the queue, to check
for the existence of a product.
Since the end of its wait
corresponds to the locking of the mutex, it does not run the risk of
having the new product stolen before it takes it.<BR>
<BR>

<H4> Readers and Writers</H4>
<A NAME="@concepts357"></A>
Here is another classic example of concurrent processes in which the
agents do not have the same behaviour with respect to the shared data.<BR>
<BR>
A writer and some readers operate on some shared data. The action of
the first may cause the data to be momentarily inconsistent, while the
second group only have a passive action. The difficulty arises from
the fact that we do not wish to prohibit multiple readers from
examining the data simultaneously. One solution to this problem is
to keep a counter of the number of readers in the processes of
accessing the data. Writing is not allowed except if the number of
readers is 0.<BR>
<BR>
The data is symbolized by the integer <TT>data</TT> which takes the
value <EM>0</EM> or <EM>1</EM>. The value <EM>0</EM> indicates that
the data is ready for reading:<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>data<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>0</CODE><CODE> </CODE>;;<BR><CODE>val data : int ref = {contents=0}</CODE><BR>

</PRE>
<BR>
<BR>
Operations on the counter <TT>n</TT> are protected by the mutex <TT>m</TT>:


<PRE><BR># <B>let</B><CODE> </CODE>n<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>0</CODE><CODE> </CODE>;;<BR><CODE>val n : int ref = {contents=0}</CODE><BR># <B>let</B><CODE> </CODE>m<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Mutex.create<CODE> </CODE>()<CODE> </CODE>;;<BR><CODE>val m : Mutex.t = &lt;abstr&gt;</CODE><BR># <B>let</B><CODE> </CODE>cpt_incr<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<CODE> </CODE>incr<CODE> </CODE>n<CODE> </CODE>;<CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<BR><CODE>val cpt_incr : unit -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>cpt_decr<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<CODE> </CODE>decr<CODE> </CODE>n<CODE> </CODE>;<CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<BR><CODE>val cpt_decr : unit -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>cpt_signal<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><CODE>!</CODE>n<CODE>=</CODE><CODE>0</CODE><CODE> </CODE><B>then</B><CODE> </CODE>Condition.signal<CODE> </CODE>c<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<BR><CODE>val cpt_signal : unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The readers update the counter and emit the signal <TT>c</TT> when no
more readers are present. This is how they indicate to the writer
that it may come into action.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Condition.create<CODE> </CODE>()<CODE> </CODE>;;<BR><CODE>val c : Condition.t = &lt;abstr&gt;</CODE><BR># <B>let</B><CODE> </CODE>read<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>cpt_incr<CODE> </CODE>()<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Reader (%d) read (data=%d)\n"</CODE><CODE> </CODE>i<CODE> </CODE><CODE>!</CODE>data<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>1</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Reader (%d) has finished reading\n"</CODE><CODE> </CODE>i<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>cpt_decr<CODE> </CODE>()<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>cpt_signal<CODE> </CODE>()<CODE> </CODE>;;<BR><CODE>val read : int -&gt; unit = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>reader<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE>read<CODE> </CODE>i<CODE> </CODE>;<CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>1</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val reader : int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The writer needs to block the counter to prevent the readers from
accessing the shared data. But it can only do so if the counter is 0,
otherwise it waits for the signal indicating that this is the
case.


<PRE><BR># <B>let</B><CODE> </CODE>write<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.lock<CODE> </CODE>m<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><CODE>!</CODE>n<CODE>&lt;&gt;</CODE><CODE>0</CODE><CODE> </CODE><B>do</B><CODE> </CODE>Condition.wait<CODE> </CODE>c<CODE> </CODE>m<CODE> </CODE><B>done</B><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE> </CODE><CODE> </CODE><CODE>"The writer is writing\n"</CODE><CODE> </CODE>;<CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>data<CODE> </CODE><CODE>:=</CODE><CODE> </CODE><CODE>1</CODE><CODE> </CODE>;<CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>1</CODE><CODE>.</CODE><TT>)</TT><CODE> </CODE>;<CODE> </CODE>data<CODE> </CODE><CODE>:=</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Mutex.unlock<CODE> </CODE>m<CODE> </CODE>;;<BR><CODE>val write : unit -&gt; unit = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>writer<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE>write<CODE> </CODE>()<CODE> </CODE>;<CODE> </CODE>Thread.delay<CODE> </CODE><TT>(</TT>Random.float<CODE> </CODE><CODE>1</CODE><CODE>.</CODE><CODE>5</CODE><TT>)</TT><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val writer : unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We create a reader and six writers to test these functions.


<PRE><BR>ignore<CODE> </CODE><TT>(</TT>Thread.create<CODE> </CODE>writer<CODE> </CODE>()<TT>)</TT>;<BR><B>for</B><CODE> </CODE>i<CODE>=</CODE><CODE>0</CODE><CODE> </CODE><B>to</B><CODE> </CODE><CODE>5</CODE><CODE> </CODE><B>do</B><CODE> </CODE>ignore<TT>(</TT>Thread.create<CODE> </CODE>reader<CODE> </CODE>i<TT>)</TT><CODE> </CODE><B>done</B>;<BR><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE>Thread.delay<CODE> </CODE><CODE>5</CODE><CODE>.</CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
This solution guarantees that the writer and the readers cannot have
access to the data at the same time. On the contrary, nothing
guarantees that the writer could ever ``fufill his offic�', there we
are confronted again with a case of starvation.<BR>
<BR>
<HR>
<A HREF="book-ora175.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora177.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
</BODY>
</HTML>
